﻿using FluentValidation.AspNetCore;
using Furion;
using Furion.SpecificationDocument;
using FurStudy.Core;
using FurStudy.Web.Core;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Serialization;
using Serilog;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace FurStudy.Web.Entry
{
    public class Startup : AppStartup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            #region 安全鉴权

            // 添加Cookie单独身份验证
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, b =>
                {
                    b.LoginPath = "/Home/Login";
                });

            // 添加Jwt身份验证
            services.AddJwt(); // 注：.AddJwt() 必须在 .AddControllers() 之前注册

            // 混合身份验证
            // JWT和Cookies混合身份验证
            services.AddJwt(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            }).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
             {
                 options.LoginPath = "/Home/Login";
             });

            // 高级自定义授权 - AppAuthorizeHandler
            services.AddJwt<JwtHandler>();

            // 全局授权
            services.AddJwt<JwtHandler>(enableGlobalAuthorize: true);

            #endregion

            services.AddControllers()
                    .AddNewtonsoftJson()
                    .AddInjectWithUnifyResult() // 先注册.AddNewtonsoftJson()再注册规范化结果--解决Swagger出现默认xml参数问题解决
                    .AddFluentValidation(fv => {
                        fv.RegisterValidatorsFromAssemblies(App.Assemblies); // 注册FluentValidation，集成第三方校验组件
                    });

            // 自定义逻辑控制Swagger每一个api可见性
            services.AddInject(options =>
            {
                // 规范化结果配置
                options.SpecificationDocumentConfigure = spt =>
                {
                    // Swagger生成器配置
                    spt.SwaggerGenConfigure = gen =>
                    {
                        gen.DocInclusionPredicate((currentGroup, apiDescription) =>
                        {
                            // Furion内部检查，必须放在第一行
                            var isShow = SpecificationDocumentBuilder.CheckApiDescriptionInCurrentGroup(currentGroup, apiDescription);

                            // 获取当前方法
                            _ = apiDescription.TryGetMethodInfo(out var method);

                            // 有了方法，这里做你想做的事情，isShow设置true可见，设置false不可见
                            return isShow;
                        });
                    };
                };
            });

            // 注册远程请求服务
            services.AddRemoteRequest(options =>
            {
                // 配置 Github 基本信息
                options.AddHttpClient("github", c =>
                {
                    c.BaseAddress = new Uri("https://api.github.com/");
                    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
                    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
                });

                // 多个请求客户端配置
                // Furion框架也提供了多个请求客户端配置，可以为多个客户端请求配置默认请求信息，如

                // 配置Facebook基本信息
                options.AddHttpClient("facebook", c => 
                {
                    c.BaseAddress = new Uri("https://api.facebook.com/");
                    c.DefaultRequestHeaders.Add("Accept", "application/vnd.facebook.v3+json");
                    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
                });

                // 配置更多参数，比如SSL证书
                services.AddHttpClient("github", c =>
                {
                    // 其他配置
                })
                .ConfigurePrimaryHttpMessageHandler(u => new HttpClientHandler
                {
                    // 忽略 SSL 不安全检查，或 https 不安全或 https 证书有误
                    ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
                    ClientCertificateOptions = ClientCertificateOption.Manual,
                    ClientCertificates = {
                        new X509Certificate2("...","..."),
                        new X509Certificate2("...","..."),
                        new X509Certificate2("...","...")
                    }
                });

                // 关于默认客户端配置
                // 默认情况下，services.AddRemoteRequest()会注册一个默认的客户端，但是无法进行默认客户端更多配置，如需配置默认客户端，可以设置inludeDefaultHttpClient参数为false，并手动注册默认客户端，如：

                // 配置默认客户端，注意string.Empty
                services.AddHttpClient(string.Empty, c => 
                {
                    c.BaseAddress = new Uri("https://furion.pro/");
                    // 其他配置
                });
            }, false); // 设置inludeDefaultHttpClient

            // 注册多语言
            services.AddControllersWithViews().AddAppLocalization();

            // 全局配置序列化(System.Text.Json)
            services.AddControllersWithViews().AddJsonOptions(options => 
            {
                options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; // 不区分大小写
                options.JsonSerializerOptions.PropertyNamingPolicy = null; // 属性名大写(属性原样输出)  // 采用Newtonsoft.Json方式接口返回值能够正常输出，但是Swagger界面中的Example Values依然显示小写字母开头的属性，这时只需要再添加System.Text.Json配置即可，主要原因是Swagger拓展包底层依赖了System.Text.Json
                options.JsonSerializerOptions.Converters.AddDateFormatString("yyyy-MM-dd HH:mm:ss"); // 时间格式化
                // options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; // 忽略循环引用  此条仅限于 .NET 6 Preview 2+
                options.JsonSerializerOptions.IncludeFields = true; // 包含成员字段序列化
                options.JsonSerializerOptions.AllowTrailingCommas = true; // 允许尾随逗号
                options.JsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; // 允许注释
                options.JsonSerializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // 处理乱码问题
            });

            // 自定义序列化提供器(Newtonsoft.Json)
            services.AddControllersWithViews().AddNewtonsoftJson(options => 
            {
                options.SerializerSettings.ContractResolver = new DefaultContractResolver(); // 属性原样输出
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; // 时间格式化
                options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; // 忽略循环引用
            });

            // 添加即时通讯
            services.AddSignalR();

            // 注册脱敏词汇检测服务
            services.AddSensitiveDetection();

            // 注册自定义脱敏提供器
            services.AddSensitiveDetection<FurStudySensitiveDetectionProvider>();

            // 注册虚拟文件系统服务
            services.AddVirtualFileServer();

            // 注册Session服务
            services.AddSession(options => 
            {
                options.IdleTimeout = TimeSpan.FromSeconds(10);
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            }); // 注意在控制器之前注册！！！！！！

            // 防止Session ID 改变或 Session失效
            services.Configure<CookiePolicyOptions>(options => 
            {
                options.CheckConsentNeeded = context => true; // 默认为true，改为false
                options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.None;
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseAuthentication();
            app.UseAuthorization();

            // 配置多语言，必须在路由注册之前
            app.UseAppLocalization();

            app.UseStaticFiles();
            app.UseSerilogRequestLogging(); // 添加请求日志中间件  // 必须在UseStaticFiles和UseRouting之间
            app.UseRouting();

            app.UseSession(); // 中间件注册顺序：app.UseSession()必须在app.UseRouting()和app.UseEndpoints()之间注册！

            app.UseEndpoints(endpoints => 
            {
                // 注册集线器
                endpoints.MapHubs();
                endpoints.MapHub<ChatHub>("/hubs/chathub");

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

            // 默认静态资源调用，wwwroot
            app.UseStaticFiles();

            // 配置模块化静态资源
            app.UseFileServer(new FileServerOptions 
            {
                FileProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly()), // Assembly.GetEntryAssembly()此处替换具体的模块程序集
                RequestPath = "/模块名称", // 后续所有资源都是通过 /模块名称/xxx.css 调用
                EnableDirectoryBrowsing = true
            });
        }
    }
}
